package resources;

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;

/**
 * An image of images that can be called and used individually, and supports transparency
 * 
 * @author David Insley
 * @version 1.0 2011-04-02
 */
public class ImageSet {
	
	/** 
	 * The type of image resource this is
	 */
	Resources.ResourceType type;
	
	/** 
	 * The size of a single image in pixels
	 */
	private int imageWidth, imageHeight;

	/** 
	 * The area around the image before you start the sprites: left, top, right, bottom
	 */
	private int[] margin = {0, 0, 0, 0};  

	/** 
	 * The array of sprites from this spritesheet 
	 */
	private ImageResource[][] images;

	/** 
	 * The spacing between sprites 
	 */
	private int spacing;

	/** 
	 * The target image for this sheet 
	 */
	private BufferedImage image;

	/** 
	 * The filter that will be used to set the transparency color of this spritesheet 
	 */
	private TransparencyFilter filter = new TransparencyFilter();

	/**
	 * Creates a new image set with the indicates settings based on the relative path to the image, as well as setting the transparency color with {@link #setTransparencyColor(Color)}
	 * 
	 * @param path The path to the image
	 * @param spriteWidth The width of a image, in pixels
	 * @param spriteHeight The height of a image, in pixels
	 * @param spacing The width of the spacing between sprites, in pixels
	 * @param margin The width of the area around the image before sprites appear, in pixels
	 * @param transparency The color to be transparent in this imageset
	 * 
	 */
	public ImageSet(Resources.ResourceType type, String path, int spriteWidth, int spriteHeight, int spacing, int[] margin, Color transparency) {
		this.imageWidth = spriteWidth;
		this.imageHeight = spriteHeight;
		this.margin = margin;
		this.spacing = spacing;
		setTransparencyColor(transparency);
		try {
			image = ImageIO.read(new File(path));
			setSheetImage(image);
		} catch (IOException e) {
			e.printStackTrace();
		}
		loadImageSet();
	}


	public ImageSet(Resources.ResourceType type, String path, int spriteWidth, int spriteHeight, int spacing, int margin, Color transparency) {
		this(type, path, spriteWidth, spriteHeight, spacing, new int[] {margin, margin, margin, margin}, transparency);
	}


	/**
	 * Loads the sprites of the sheet image into the BufferedImage array.
	 * First applies the current transparency settings to the image, then calculates how many sprites are in this sheet.
	 * Then loops through and calls {@link #getSprite(int, int)} to populate the array.
	 */
	private void loadImageSet() {	
		applyTransparency();
		int xImages = ((getWidth()-(margin[0] + margin[2]) - imageWidth) / (imageWidth + spacing)) + 1;
		int yImages = ((getHeight()-(margin[1] + margin[3]) - imageHeight) / (imageHeight + spacing)) + 1; 

		if ((getHeight() - imageHeight) % (imageHeight+spacing) != 0) {
			yImages++;
		}

		images = new ImageResource[xImages][yImages];

		for (int x=0; x< xImages; x++) {
			for (int y=0; y< yImages; y++) {
				images[x][y] = getImage(x,y);
			}
		}
	}

	public int[] getImageResourceBounds(int x, int y) {
		return new int[]{x * (imageWidth + spacing) + margin[0], y * (imageHeight + spacing) + margin[1], imageWidth, imageHeight};
	}

	/**
	 * Gets a BufferedImage based on the location of a image calculated using the sheet settings: {@link #imageWidth}, {@link #imageHeight}, {@link #spacing}, {@link #margin}. 
	 * 
	 * @param x The column the image is in
	 * @param y The row the image is in
	 */
	public ImageResource getImage(int x, int y) {
		if ((x < 0) || (x >= images.length) || (y < 0) || (y >= images[0].length)) {
			System.out.println("Image index out of bounds!");
		}
		if(images[x][y] != null) {
			return images[x][y];
		}
		int[] bounds = getImageResourceBounds(x, y);
		BufferedImage img = image.getSubimage(bounds[0], bounds[1], bounds[2], bounds[3]);
		return new ImageResource(this, img, x, y); 
	}

	/**
	 * Applys a filter to the BufferedImage sheet and sets the sheet image to the transparent one
	 */
	public void applyTransparency() {
		ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
		Image image = Toolkit.getDefaultToolkit().createImage(ip);
		BufferedImage dest = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2 = dest.createGraphics();
		g2.drawImage(image, 0, 0, null);
		g2.dispose();
		setSheetImage(dest);
	}

	/**
	 * Sets the direct BufferedImage of this image set
	 * Does not reload sprites
	 * 
	 * @param img The BufferedImage to set as the image set source 
	 */
	public void setSheetImage(BufferedImage img) {
		image = img;
	}

	/**
	 * Sets the transparency color the image filter will use
	 * 
	 * @param transparent The color to make transparent 
	 */
	public void setTransparencyColor(Color transparent) {
		filter.setTransparencyColor(transparent);
	}

	/**
	 * @return The BufferedImage representing this entire set
	 */
	public BufferedImage getImage() {
		return image;
	}

	/**
	 * @return The number of image columns in this sprite sheet
	 */
	public int getColumnCount() {
		return images.length;
	}

	/**
	 * @return The number of image rows in this sprite sheet
	 */
	public int getRowCount() {
		return images[0].length;
	}

	/**
	 * @return The total number of images in this sprite sheet
	 */
	public int getSpriteCount() {
		return images.length * images[0].length;
	}

	/**
	 * @return The total width of the sprite sheet, including margins and spacing
	 */
	public int getWidth() {
		return image.getWidth();
	}

	/**
	 * @return The total height of the sprite sheet, including margins and spacing
	 */
	public int getHeight() {
		return image.getHeight();
	}

	public int getImageWidth() {
		return imageWidth;
	}

	public void setImageWidth(int imageWidth) {
		this.imageWidth = imageWidth;
	}

	public int getImageHeight() {
		return imageHeight;
	}

	public void setImageHeight(int imageHeight) {
		this.imageHeight = imageHeight;
	}

	public BufferedImage getImagesOnly() {
		BufferedImage temp = new BufferedImage(getColumnCount()*imageWidth, getRowCount()*imageHeight, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = temp.createGraphics();
		for(ImageResource[] ti2 : images) {
			for(ImageResource ti : ti2) {
				g.drawImage(ti.getImage(), null, imageWidth*ti.getSheetX(), imageHeight*ti.getSheetY());
			}
		}
		return temp;
	}


	private class TransparencyFilter extends RGBImageFilter {

		/** The RGB value of the color we wish to be transparent for this filter*/
		private int transparentRGB = Color.MAGENTA.getRGB() | 0xFF000000;

		/**
		 * Sets the transparency color this filter will use
		 * 
		 * @param color The color to make transparent 
		 */
		public void setTransparencyColor(Color color) {
			transparentRGB = color.getRGB() | 0xFF000000;
		}

		public final int filterRGB(int x, int y, int color) {
			if ((color | 0xFF000000) ==  transparentRGB) {
				return 0x00FFFFFF & color;
			}
			return color;
		}
	}

}
